package toster;

import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author zach
 */
public class PreProssesor {
   volatile public engine horz;
   volatile public engine vert;
   //base case
   volatile public int[][] horizontal_rules;
   volatile public int[][] vertical_rules;
   private GridListener[] Grid;
   Thread zig_one;
    Thread zig_two;
    Thread zig_three;
    Thread zig_four;
    BaseCase basecase;
    private volatile Perms[] rowPerms, colPerms;
    private volatile int[][] rowKnown, colKnown;
    int rows, cols;

    /**
     * @param horizontal_basecase
     * @param vertical_basecase
     */
    public PreProssesor(int[][] horizontal,int[][] vertical) {
        this.horizontal_rules = horizontal;
        this.vertical_rules = vertical;
        horz=new engine(new int[vertical.length][horizontal.length],new int[horizontal.length][vertical.length]);
        vert=new engine( new int[horizontal.length][vertical.length],new int[vertical.length][horizontal.length]);

        EventHorizon event=new EventHorizon();

       

        //making the threads please don't make more, just reuse old ones.
       // zig_one=new pass0(this.horizontal_basecase,vertical_basecase,horz);
       // zig_two=new pass0(vertical_basecase,this.horizontal_basecase,vert);

         //starting the threads.
         //zig_one.start();
         //zig_two.start();
         //there is no scence crying over every mistake
        /* while(zig_one.isAlive()&&zig_two.isAlive()){
             //we just keep on trying until we run out of cake
             try{
             Thread.sleep(100);
             }catch(Exception e){
                 System.out.println(e);
             }
         } */
         //this should be getting everything consistan across the diffrent engins
        Function.merge(horz.horizontal_basecase, vert.horizontal_basecase);
        Function.merge(horz.vertical_basecase, vert.vertical_basecase);

        rows = horizontal.length;
        cols = vertical.length;
        

        initPerms(horizontal, vertical);
        basecase = new BaseCase(rows, cols);
        firstPass(basecase);
         Grid=new GridListener[rows+cols];
        int q=0;
        for(int i=0;i<horz.horz.length;i++){
            Grid[q]=new GridListener(horz.horz[i],true,i,horz.horizontal_basecase,horz.vertical_basecase,event);
            Grid[q]=new GridListener(vert.vert[i],true,i,vert.vertical_basecase,vert.horizontal_basecase,event);
            q++;
        }

        for(int i=0;i<rows;i++){
            Grid[q]=new GridListener(horz.vert[i],false,i,horz.vertical_basecase,horz.horizontal_basecase,event);
            Grid[q]=new GridListener(vert.horz[i],false,i,vert.horizontal_basecase,vert.vertical_basecase,event);
            q++;
        }
          Function.merge(horz.horizontal_basecase, basecase.grid);
          Function.merge(vert.horizontal_basecase, basecase.grid);
          Function.merge(horz.vertical_basecase, Function.FilpFlop(basecase.grid));
          Function.merge(horz.vertical_basecase, Function.FilpFlop(basecase.grid));
        //the second pass starts the third pass
        secondPass(basecase,event);
          Function.merge(horz.horizontal_basecase, basecase.grid);
          Function.merge(vert.horizontal_basecase, basecase.grid);
          Function.merge(horz.vertical_basecase, Function.FilpFlop(basecase.grid));
          Function.merge(horz.vertical_basecase, Function.FilpFlop(basecase.grid));

      PermGen  hperms = new PermGen(basecase, true, horizontal_rules, rowPerms);
      PermGen  vperms = new PermGen(basecase, false, vertical_rules, colPerms);
        hperms.run();
        vperms.run();

        while(hperms.isAlive()&&vperms.isAlive()){
             //we just keep on trying until we run out of cake
             try{
             Thread.sleep(100);
             }catch(Exception e){
                 System.out.println(e);
             }
         }
        
        try {
            hperms.join();
            vperms.join();
        } catch (InterruptedException ex) {
            Logger.getLogger(PreProssesor.class.getName()).log(Level.SEVERE, null, ex);
        }

        horz.horz=hperms.getPermutations();
        vert.horz=hperms.getPermutations();

        horz.vert=vperms.getPermutations();
        vert.vert=vperms.getPermutations();
        //TODO figure out what this was doing
        /*
        for(int i=0;i<horz.horizontal_basecase.length;i++){
            if(Function.countEqualTo(horz.horizontal_basecase[i], Function.filled)==0){//if there is noting filled
                //TODO add the convesion to a convert to boolean string
                int[][] zig=Function.PowerSet(Function.permset(this.horizontal_basecase[i],this.horizontal_basecase[i].length));

                //horz.horz[i].add();
            }else{
                horz.horz[i]=null;
                vert.horz[i]=null;
            }
        }
        */

        //this will die when the third pass is done
        zig_one=new WatchedPot();

        while(zig_one.isAlive()){
             //we just keep on trying until we run out of cake
             try{
             Thread.sleep(100);
             }catch(Exception e){
                 System.out.println(e);
             }
         }

         
        Colider colider=new Colider();
        new Output(colider.Do(horz.Power_set(true),vert.Power_set(false)));
    }

    /**
     * Runs the first pass on the data, stores result in BaseCase.
     * Processes each row and column once. Fills in everything possible without
     * knowing stuff from other instances of the passes. If given the block [4]
     * and the length 6, it would fill in the middle 2 squares.
     * @param basecase The BaseCase where the result is stored
     */
    private void firstPass(BaseCase basecase) {
        
        int[] row; // For each row:
        for (int index = 0; index < rows; index++) {
            row = Function.merge(basecase.getRow(index),
                    Function.simpleBoxes(horizontal_rules[index], cols));
            basecase.setRow(index, row);
        }

        int[] col; // For each col:
        for (int index = 0; index < cols; index++) {
            col = Function.merge(basecase.getCol(index),
                    Function.simpleBoxes(vertical_rules[index], rows));
            basecase.setCol( index, col);
        }
    }



    /**
     * The second pass of processing the data. Stores result in BaseCase.
     * Relies partially on the data already generated in firstPass().
     * Marks empty squares as empty iff all the filled squares for the row / col
     * have been filled in.
     * @param basecase The BaseCase where the result is stored
     */
    private void secondPass(BaseCase basecase,EventHorizon EH) {
        int index;

        int[] rowTemp; // For each row:
        for (index = 0; index < rows; index++) {
            
            rowTemp = Function.superMerge(
                    basecase.getRow(index),
                    Function.fillEmptySpaces(horizontal_rules[index], basecase.getRow(index)),EH,index,true);
            basecase.setRow(index, rowTemp);
        }

        int[] colTemp; // For each col:
        for (index = 0; index < cols; index++) {
            
            colTemp = Function.superMerge(
                    basecase.getCol(index),
                    Function.fillEmptySpaces(vertical_rules[index], basecase.getCol(index)),EH,index,false);
            basecase.setCol(index, colTemp);
        }
    }

    class pass0 extends Thread{
       private int[][] arrayh;
       private int[][] arrayv;
       private engine engine;
        public pass0(int[][] arrayh,int[][] arrayv, engine engine) {
            super();
            this.arrayh = arrayh;
            this.engine = engine;
            this.arrayv=arrayv;
        }

//making things point to the right stuff yay dsylixia
        public void run(){
            for(int i=0;i<engine.horizontal_basecase.length;i++){
                if(Function.perms(engine.horizontal_basecase[i],engine.horizontal_basecase[i].length )==1){
                    boolean[] zig=Function.convert(Function.PowerSet(Function.permset(arrayh[i],cols))[0]);
                    engine.horizontal_basecase[i]=Function.convert(Function.permutation(zig, arrayh[i],engine.horizontal_basecase[i].length ));
                }
            }

            for(int i=0;i<engine.vertical_basecase.length;i++){
                if(Function.perms(arrayv[i],engine.vertical_basecase[i].length )==1){
                    boolean[] zig=Function.convert(Function.PowerSet(Function.permset(arrayv[i],rows))[0]);
                    engine.vertical_basecase[i]=Function.convert(Function.permutation(zig, arrayv[i],engine.vertical_basecase[i].length ));
                }
        }
        }
    }

    

    //TODO figure out why this is here, it is in the engine class
    private void initPerms(int[][] horizontal, int[][] vertical) {
        rowPerms = new Perms[rows];
        colPerms = new Perms[cols];
        for (int i = 0; i < rows; i++) {
            rowPerms[i] = new Perms((int) 
                    Function.perms(horizontal[i], cols));
        }
        for (int i = 0; i < cols; i++) {
            colPerms[i] = new Perms((int)
                    Function.perms(vertical[i], rows));
        }
    }
    
    private class PermGen extends Thread {

        private BaseCase bc;
        private boolean isHorz;
        private int[][] rule;
        private Perms[] permutations;

        public Perms[] getPermutations(){
            return permutations;
        }

        public PermGen(BaseCase b, boolean ishorz,
                int[][] rule, Perms[] storage) {
            super();
            bc = b;
            this.isHorz = ishorz;
            this.rule = rule;
            permutations = storage;
        }

        @Override
        public void run() {
            int len = rule.length, rowLength;
            int[] known;

            if (isHorz) rowLength = bc.rowLen;
            else rowLength = bc.colLen;

            for (int i = 0; i < len; i++) {
                known = getSection(i);
                
                permutations[i]=new Perms(Function.convert(Function.filteredSet(known, rule[i])));
                
                
            }

        }

        private int[] getSection(int index) {
            if (isHorz) return bc.getRow(index);
            else return bc.getCol(index);
        }

    }
    
}
